Procedures
Declare, Subroutines & Functions Declare statements allow a one-pass compiler to develop a list of user symbols to process source code and provide information about external modules such as system and other .dlls and linkable .obj modules. For procedures defined in your source code: DECLARE SUB | FUNCTION name As type[, param2 As type[, etc])] type For procedures defined in external .dll or .obj modules: DECLARE SUB | FUNCTION name LIB "libname" "aliasname" As type[, param2 As type[, etc])] type The syntax above may look complicated, but hold your breath. Items in . are optional. DECLARE FUNCTION requires "As type" at the end. ALIAS is used only when LIB is defined and is optional. "_" continues a statement on the next line. Let's start with simple examples: DECLARE SUB Declare SUB MySub 'code Call MySub 'code SUB MySub 'here we define what we do in MySub 'code IF {condition} THEN Exit Sub 'more code END SUB Above, MySub is declared and defined. In the SUB code, one can EXIT SUB at any time. In the DECLARE statements, type is the variable type. Please see "Dimension". The symbols used for param1, param2, etc are dimensioned automatically, as shown in compiler Symbol Table output. DECLARE FUNCTION Now a simple FUNCTION example: DEFREAL10 x,y,z Declare FUNCTION MyFun As LONG 'code x=100: y=2: z = MyFun 'code FUNCTION MyFun As LONG 'MyFun can access dimensioned variables and does not need arguments RESULT = -1 'if we want to set a default result 'code IF x > 0 AND y > 0 THEN RESULT = x / y ELSE Exit FUNCTION 'MyFun returns either -1 or x/y 'more code END FUNCTION In the FUNCTION code, you can also assign the result to the name of the function. Above, MyFun = -1 would be the same as RESULT = -1. Some compilers do not flag missing result statements, but instead "invent" a return value of zero. Such compilers are a case study in both insecure and inefficient computing. In HotBasic, absence of a defined function result as described above is assumed to be a source code error, so you don't waste time debugging aprogram that does not work because of a missing result statement. If your SUB or FUNCTION has arguments, you just add them in (... ). A simple SUB or FUNCTION naming convention implements PROPERTY SET and PROPERTY GET as described in "Variables and Types". Calling Windows API functions (hotapi.bas) What if the SUB or FUNCTION is not defined in your own code? API calls are illustrated in hotapi.bas, and may be as simple as: Declare FUNCTION GetLastError LIB "kernel32" ALIAS "GetLastError" () _ As LONG Declare FUNCTION Beep LIB "kernel32" (BYVAL freq As LONG,_ BYVAL duration As LONG) As LONG DIM rval As LONG 'code which might cause an error condition rval=GetLastError rval=Beep(400,2000) "BYVAL" is not necessary and may be omitted. The LIB is kernel32.dll and the ALIAS may be defined or omitted. ALIAS allows you to refer to the procedure with one name in your code, but HotBasic will substitute the ALIAS name, if present, at compile time. All API function arguments are 32-bit quantities. However, if the declared argument is "As STRING", "As ", or "As VARIANT", HotBasic automatically posts the argument as a pointer. In addition, "As DOUBLE" and "As INT64" may also be used, if the API specifies a DOUBLE or INT64 argument. "BYREF" in the argument list of a DECLARE ... LIB statement provokes a Warning compile ErrorLevel and is otherwise ignored. Indeed, it is not needed for STRING, VARIANT or UDT arguments, since a pointer automatically is used. For other pointer arguments, use "As DWORD" in the DECLARE statement and the pointer as the argument in the CALL statement (Please see @ and VARPTR in Numeric Functions.) Calling Procedures in a Non-Windows .DLL (hotcall.bas and hotdll.dll) hotcall.bas shows that a HotBasic executable can use funtions in a .DLL compiled by HotBasic or obtained elsewhere. The required Declare statements are identical in form. HotBasic provides two run-time errors: (1) LIB .dll file not found and (2) function name not found in LIB .dll. Calling Procedures in .OBJ Modules (hotlink.bas and hotobj.obj) How do we link an .obj module compiled by HotBasic or obtained elsewhere? For each procedure in the external module, we have a DECLARE like above with one crucial difference: the full name of the file is specified. Declare SUB DoNeatThing LIB "neatthings.obj" (args) 'code DoNeatThing(args) 'or Call DoNeatThing(args) calls the subroutine. or use a Declare FUNCTION as above, but with LIB "neathings.obj". The .obj file is included in the executable produced. If .obj modules are stored in a .lib file, then, in the example above, "neatthings.obj" would be changed to "neatthings.lib". Run this .bat program from your HotBasic directory to make a .lib file from all .obj modules (from $APPTYPE OBJ) in a directory: HBlib.bat Echo Usage: HBlib Obj_File_Path Lib_Name Echo Example: HBlib c:\hot\trig trigfunc bin\link -lib "%1\*.obj" "/out:%2.lib" Above, all the .obj files in c:\hot\trig are placed in trigfunc.lib in your HotBasic directory. Copy trigfunc.lib to your project directory to link its modules with your program. If the SUB has arguments, you may use (..) or not, but no space before (. DoNeatThing(MyValue1, MyValue2, etc) 'or DoNeatThing MyValue1, MyValue2, etc Note: If the external module is not stdcall (Windows compatible), you may have to DECLARE it without arguments and use PUSH to place the arguments on the stack in the correct order before calling the SUB or FUNCTION. Please see Statements > Advanced Techniques. CDECL calling convention often used in C language software Merely change the DECLARE keyword to CDECL to use CDECL calling conventions. CDECL FUNCTION xyz LIB "abc.lib" ... etc where abc.lib contains procedures compiled for CDECL calling conventions -- which are the same as STD (stdcall), except arguments are not cleaned from the stack. CDECL FUNCTION xyz STD (...args...) As LONG where CDECL may be used to prepare procedures used for CDECL callbacks and for $APPTYPE DLL or OBJ which may be used in a C language environment. Usage is simple: just use CDECL in place of the DECLARE keyword. CDECL has an effect only in declaration statements with LIB or STD keywords. Summary where CDECL or DECLARE may be used in the examples For procedures in $APPTYPE DLL or OBJ or for certain callback procedures in your main program, the STD keyword may be used and HotBasic will manage argument retrieval from the stack. STD is positioned where LIB might otherwise appear in a DECLARE statement -- after the procedure name and before any arguments. Please see example in hotstd.bas in HotThing. In $APPTYPE DLL or OBJ, only DECLARE ... STD procedures may be called by code in another module ("public"). That is, other DECLARE and GOSUB procedures are "private". For $APPTYPE DLL, only procedures with DECLARE ... STD statements are exported. All DECLARE statements for STD procedures will have arguments equivalent to 4-byte values, exactly as in Microsoft API function definitions. That is, a STRING, VARIANT or UDT is always a pointer (DWORD), numbers are always INTEGER, DWORD, etc. However, DOUBLE and INT64 arguments are posted on the stack as two DWORDs. When the procedure is called, a BYTE, WORD or SHORT value may be used as an argument, HotBasic will convert these to the type specified in the DECLARE or CDECL statement for the procedure. STD can be used for callback procedures. Example: DECLARE SUB MyWndProc STD (hwin as DWORD, pmsg as DWORD, wpar as DWORD, lpar as DWORD) and the RETVAL keyword is used to return a result in the CPU eax register. Callback procedures may also be declared as FUNCTION; however, DECLARE SUB and use of RETVAL on exit is more efficient. GOSUB With GOSUB, no DECLARE is needed (please see HotBasic Tips) and any LABEL may start, or be an alternate entry point into, a subroutine. GOSUB ThisSub: GOSUB ThatSub 'code ThisSub: 'this is just a LABEL 'code RETURN ThatSub: 'code A ThatSubEntry2: 'code B RETURN Notice that GOSUB ThatSub runs code A and B, while using the alternate entry point -- GOSUB ThatSubEntry2, runs code B only. If you place all your GOSUB (and similer GUI event) procedures after the END statement, your program flow will not "fall into" this code. After all source code is processed, HotBasic checks if all GOTO, GOSUB and event procedure LABELS exist. A line LABEL may help describe what a SUB does: GOSUB ComputeIncome. But it could be an integer number, too, as in older versions of Basic (example in hottest.bas). SUMMARY Code for a SUB/FUNCTION in your main program module or in an external module (a linked .obj or .lib file, a .dll, .ocx, .ax, etc) may be viewed as Public or Private. Generally, only Public procedures may be called from another module and therefore arguments must be placed on the stack. Within any module, code may call either its Public or Private procedures. see HotBasic Tips for more detail on when and how the stack is used for arguments. (1) Private: All variable types and UDT's can be used as arguments in SUB/FUNCTION DECLARE and CALL statements. Arguments are not placed on the stack -- an inefficient practice that makes most other compilers obsolete. For FUNCTION return values, all variable types may likewise be used (except VARIANT and UDT). GOSUB procedures are all Private. (2) Public: DECLARE statements contain the LIB or STD keyword. DECLARE arguments are 4-byte variables, "As STRING", "As VARIANT", "As " or "As DOUBLE". If an argument is STRING, VARIANT or UDT, its pointer is automatically used. Usually, the result of a Public FUNCTION is a 32-bit value (e.g., As LONG). However, HotBasic will autodetect the result of a LIB or STD FUNCTION declared "As STRING" and assume the return value is a pointer which is delivered to the calling code as a STRING data type. Note: a STD callback procedure acts like the Public category above. Though it may be located in your main program, it is called by an external module -- usually the OS.